package junior.util.address;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;

/**
 * 用于增强{@link AddressToken}的分词能力，同时可以匹配地址和指定类型(T)的数据
 * 
 * @param <T> 指定类型的数据和地址进行匹配
 * @author ZhangXiaoye
 * @date 2016年11月19日 下午3:53:04
 */
public class AddressIndex<T> implements Serializable{
	
	private static final long serialVersionUID = 1L;

	final List<Pair<Address, T>> list = new ArrayList<Pair<Address,T>>();
	
	final Map<AddressToken, Set<Integer>> indexMap = new HashMap<AddressToken, Set<Integer>>();
	
	final Map<String, Set<AddressToken>> primaryMap = new HashMap<String, Set<AddressToken>>();
	
	final Map<AddressTokenLevel, Integer> levelCount = new EnumMap<AddressTokenLevel, Integer>(AddressTokenLevel.class);
	
	/**
	 * 加入一个地址和数据
	 * 
	 * @param value 数据
	 * @param tokens 地址
	 * @author ZhangXiaoye
	 * @date 2016年11月19日 下午3:53:55
	 */
	public void put(T value, List<AddressToken> tokens){
		final int id = list.size();
		final Address addr = new Address(tokens);
		list.add(Pair.of(addr, value));
		
		for(AddressToken token: tokens){
			// indexMap
			Set<Integer> idSet = indexMap.get(token);
			if(idSet == null){
				idSet = new TreeSet<Integer>();
				indexMap.put(token, idSet);
			}
			idSet.add(id);
			// primaryMap
			final String primary = token.getPrimary();
			Set<AddressToken> tokenSet = primaryMap.get(primary);
			if(tokenSet == null){
				tokenSet = new HashSet<AddressToken>();
				primaryMap.put(primary, tokenSet);
			}
			tokenSet.add(token);
			// levelCount
			Integer c = levelCount.get(token.getLevel());
			if(c == null){
				levelCount.put(token.getLevel(), 1);
			}else{
				levelCount.put(token.getLevel(), c + 1);
			}
		}
	}
	
	/**
	 * @see {@link #put(Object, List)}
	 */
	public void put(T value, AddressToken... tokens){
		put(value, Arrays.asList(tokens));
	}
	
	/**
	 * @see {@link #put(Object, List)}
	 */
	public void put(T value, String... tokens){
		int level = 0;
		List<AddressToken> tokenList = new ArrayList<AddressToken>(tokens.length);
		for(String t: tokens){
			if(StringUtils.isBlank(t)){
				continue;
			}
			AddressToken token = null;
			for(AddressTokenLevel l: AddressTokenLevel.values()){
				Integer nextLevel = AddressTokenLevel.isPossibleRouting(level, l);
				if(nextLevel != null){
					String suffix = null;
					for(int i = 1; i < t.length(); i ++){
						final String subT = t.substring(i);
						final int idx = Arrays.binarySearch(l.suffixs, subT);
						if(idx >= 0){
							suffix = l.suffixs[idx];
							break;
						}
					}
					if(suffix != null){
						token = new AddressToken(l, t, suffix, nextLevel);
						level = nextLevel;
						break;
					}
				}
			}
			if(token != null){
				tokenList.add(token);
			}else{
				tokenList.add(new AddressToken(AddressTokenLevel.UNKNOW, t));
			}
		}
		put(value, tokenList);
	}
	
}
